import asyncio
import json
import hashlib
import hmac
import time
from datetime import datetime, timedelta
from typing import Dict, Any, Optional, List
from sqlalchemy.ext.asyncio import AsyncSession
import aiohttp
from aiohttp import ClientTimeout

from module_speech.dao.speech_dao import SpeechRecordDao
from module_speech.entity.do.speech_record_do import SpeechRecordDO
from utils.log_util import logger
from exceptions.exception import ServiceException


class CallbackService:
    """
    回调服务层
    """
    
    # 回调配置
    CALLBACK_TIMEOUT = 30  # 秒
    MAX_RETRY_TIMES = 3
    RETRY_INTERVALS = [1, 5, 15]  # 重试间隔（秒）
    
    @classmethod
    async def send_async_callback(cls, db: AsyncSession, speech_record: SpeechRecordDO,
                                 result_data: Dict[str, Any]) -> bool:
        """
        发送异步回调通知
        
        :param db: 数据库会话
        :param speech_record: 语音记录对象
        :param result_data: 识别结果数据
        :return: 是否发送成功
        """
        if not speech_record.callback_url:
            logger.debug(f"语音记录 {speech_record.record_id} 没有设置回调URL")
            return True  # 没有回调URL不算失败
        
        try:
            # 准备回调数据
            callback_data = cls._prepare_callback_data(speech_record, result_data)
            
            # 生成签名
            signature = cls._generate_webhook_signature(
                callback_data, 
                speech_record.api_secret if hasattr(speech_record, 'api_secret') else None
            )
            
            # 发送回调
            success = await cls._send_callback_request(
                speech_record.callback_url,
                callback_data,
                signature
            )
            
            # 更新回调状态
            if success:
                speech_record.callback_status = 'success'
                logger.info(f"回调发送成功: 记录ID {speech_record.record_id}")
            else:
                speech_record.callback_status = 'failed'
                logger.warning(f"回调发送失败: 记录ID {speech_record.record_id}")
            
            # 保存状态更新
            await SpeechRecordDao.update_speech_record_dao(db, speech_record)
            await db.commit()
            
            return success
            
        except Exception as e:
            logger.error(f"发送异步回调失败: {str(e)}")
            # 更新回调状态为失败
            speech_record.callback_status = 'failed'
            await SpeechRecordDao.update_speech_record_dao(db, speech_record)
            await db.commit()
            return False
    
    @classmethod
    async def retry_failed_callbacks(cls, db: AsyncSession) -> Dict[str, int]:
        """
        重试失败的回调
        
        :param db: 数据库会话
        :return: 重试统计信息
        """
        try:
            # 获取需要重试的回调记录
            failed_records = await SpeechRecordDao.get_failed_callback_records(db)
            
            retry_stats = {
                'total': len(failed_records),
                'success': 0,
                'failed': 0,
                'skipped': 0
            }
            
            for record in failed_records:
                # 检查重试次数是否超限
                if cls._should_skip_retry(record):
                    retry_stats['skipped'] += 1
                    continue
                
                # 准备回调数据（从记录中重构）
                result_data = cls._reconstruct_result_data(record)
                
                # 重试发送
                success = await cls.send_async_callback(db, record, result_data)
                
                if success:
                    retry_stats['success'] += 1
                else:
                    retry_stats['failed'] += 1
                
                # 添加延迟避免频繁请求
                await asyncio.sleep(0.5)
            
            logger.info(f"回调重试完成: {retry_stats}")
            return retry_stats
            
        except Exception as e:
            logger.error(f"重试失败的回调时出错: {str(e)}")
            raise ServiceException(message=f"重试失败的回调时出错: {str(e)}")
    
    @classmethod
    async def batch_send_callbacks(cls, db: AsyncSession, 
                                  callback_tasks: List[Dict[str, Any]]) -> Dict[str, int]:
        """
        批量发送回调
        
        :param db: 数据库会话
        :param callback_tasks: 回调任务列表
        :return: 发送统计
        """
        try:
            stats = {
                'total': len(callback_tasks),
                'success': 0,
                'failed': 0
            }
            
            # 创建并发任务
            tasks = []
            for task in callback_tasks:
                speech_record = task['speech_record']
                result_data = task['result_data']
                
                task_coroutine = cls.send_async_callback(db, speech_record, result_data)
                tasks.append(task_coroutine)
            
            # 并发执行回调
            results = await asyncio.gather(*tasks, return_exceptions=True)
            
            # 统计结果
            for result in results:
                if isinstance(result, Exception):
                    stats['failed'] += 1
                elif result:
                    stats['success'] += 1
                else:
                    stats['failed'] += 1
            
            logger.info(f"批量回调发送完成: {stats}")
            return stats
            
        except Exception as e:
            logger.error(f"批量发送回调失败: {str(e)}")
            raise ServiceException(message=f"批量发送回调失败: {str(e)}")
    
    @classmethod
    async def validate_webhook_signature(cls, payload: str, signature: str, 
                                        secret: str) -> bool:
        """
        验证webhook签名
        
        :param payload: 请求载荷
        :param signature: 请求签名
        :param secret: 密钥
        :return: 签名是否有效
        """
        try:
            if not signature or not secret:
                return False
            
            # 计算期望的签名
            expected_signature = cls._generate_signature(payload, secret)
            
            # 使用安全的字符串比较
            return hmac.compare_digest(signature, expected_signature)
            
        except Exception as e:
            logger.error(f"验证webhook签名失败: {str(e)}")
            return False
    
    @classmethod
    async def get_callback_statistics(cls, db: AsyncSession,
                                     start_date: datetime = None,
                                     end_date: datetime = None) -> Dict[str, Any]:
        """
        获取回调统计信息
        
        :param db: 数据库会话
        :param start_date: 开始时间
        :param end_date: 结束时间
        :return: 统计信息
        """
        try:
            stats = await SpeechRecordDao.get_callback_statistics(db, start_date, end_date)
            return stats
            
        except Exception as e:
            logger.error(f"获取回调统计信息失败: {str(e)}")
            raise ServiceException(message=f"获取回调统计信息失败: {str(e)}")
    
    @classmethod
    async def cleanup_old_callback_records(cls, db: AsyncSession, 
                                          days_to_keep: int = 30) -> int:
        """
        清理旧的回调记录
        
        :param db: 数据库会话
        :param days_to_keep: 保留天数
        :return: 清理的记录数
        """
        try:
            cutoff_date = datetime.now() - timedelta(days=days_to_keep)
            
            # 只清理已成功的回调记录
            deleted_count = await SpeechRecordDao.cleanup_successful_callback_records(
                db, cutoff_date
            )
            await db.commit()
            
            logger.info(f"清理旧回调记录成功: 删除 {deleted_count} 条记录")
            return deleted_count
            
        except Exception as e:
            await db.rollback()
            logger.error(f"清理旧回调记录失败: {str(e)}")
            raise ServiceException(message=f"清理旧回调记录失败: {str(e)}")
    
    @classmethod
    def _prepare_callback_data(cls, speech_record: SpeechRecordDO, 
                              result_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        准备回调数据
        """
        return {
            'api_request_id': speech_record.api_request_id,
            'record_id': speech_record.record_id,
            'status': speech_record.status,
            'user_type': speech_record.user_type,
            'model_name': speech_record.model_name,
            'audio_duration': float(speech_record.audio_duration or 0),
            'result': result_data,
            'create_time': speech_record.create_time.isoformat() if speech_record.create_time else None,
            'complete_time': datetime.now().isoformat(),
            'error_message': speech_record.error_message if speech_record.status == 'failed' else None,
            'timestamp': int(time.time()),
            'version': '1.0'
        }
    
    @classmethod
    def _generate_webhook_signature(cls, data: Dict[str, Any], secret: str = None) -> str:
        """
        生成webhook签名
        """
        if not secret:
            return ""
        
        payload = json.dumps(data, sort_keys=True, separators=(',', ':'))
        return cls._generate_signature(payload, secret)
    
    @classmethod
    def _generate_signature(cls, payload: str, secret: str) -> str:
        """
        生成HMAC-SHA256签名
        """
        signature = hmac.new(
            secret.encode('utf-8'),
            payload.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
        return f"sha256={signature}"
    
    @classmethod
    async def _send_callback_request(cls, callback_url: str, data: Dict[str, Any],
                                   signature: str = None) -> bool:
        """
        发送回调请求
        """
        try:
            headers = {
                'Content-Type': 'application/json',
                'User-Agent': 'SpeechRecognition-Webhook/1.0'
            }
            
            if signature:
                headers['X-Signature'] = signature
            
            timeout = ClientTimeout(total=cls.CALLBACK_TIMEOUT)
            
            async with aiohttp.ClientSession(timeout=timeout) as session:
                async with session.post(
                    callback_url,
                    json=data,
                    headers=headers
                ) as response:
                    # 2xx状态码认为成功
                    if 200 <= response.status < 300:
                        logger.debug(f"回调发送成功: {callback_url}, 状态码: {response.status}")
                        return True
                    else:
                        logger.warning(f"回调返回错误状态码: {response.status}, URL: {callback_url}")
                        return False
                        
        except asyncio.TimeoutError:
            logger.warning(f"回调请求超时: {callback_url}")
            return False
        except aiohttp.ClientError as e:
            logger.warning(f"回调请求失败: {callback_url}, 错误: {str(e)}")
            return False
        except Exception as e:
            logger.error(f"发送回调请求时出现异常: {str(e)}")
            return False
    
    @classmethod
    def _should_skip_retry(cls, record: SpeechRecordDO) -> bool:
        """
        判断是否应该跳过重试
        """
        # 检查记录的创建时间，如果太旧就跳过
        if record.create_time:
            age_hours = (datetime.now() - record.create_time).total_seconds() / 3600
            if age_hours > 24:  # 超过24小时的记录不再重试
                return True
        
        # 检查回调状态，如果已经成功就跳过
        if record.callback_status == 'success':
            return True
        
        return False
    
    @classmethod
    def _reconstruct_result_data(cls, record: SpeechRecordDO) -> Dict[str, Any]:
        """
        从记录重构结果数据
        """
        result_data = {
            'text': record.recognized_text or '',
            'confidence': float(record.confidence_score or 0.0),
            'language': record.language or 'zh-CN',
            'duration': float(record.audio_duration or 0.0)
        }
        
        # 如果有额外的结果数据，尝试解析
        if hasattr(record, 'extra_data') and record.extra_data:
            try:
                if isinstance(record.extra_data, str):
                    extra = json.loads(record.extra_data)
                else:
                    extra = record.extra_data
                result_data.update(extra)
            except (json.JSONDecodeError, TypeError):
                pass
        
        return result_data 